Skip to content

Commit

Permalink
Merge pull request #726 from OpenZeppelin/Stake-New-Level-31
Browse files Browse the repository at this point in the history
Stake - New Level (Level 31)
  • Loading branch information
xaler5 authored Apr 15, 2024
2 parents 68136bc + ed3aa10 commit f4ae6c8
Show file tree
Hide file tree
Showing 25 changed files with 269 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ client/src/gamedata/deploy.local.json
.env

# Local Netlify folder
.netlify
.netlify
28 changes: 28 additions & 0 deletions client/public/imgs/BigLevel31.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions client/public/imgs/Level31.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/public/imgs/levels-ai.zip
Binary file not shown.
4 changes: 2 additions & 2 deletions client/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const NETWORKS = {
UNDEFINED: undefined,
LOCAL: {
name: "local",
id: "1337",
id: "31337",
url: "http://localhost",
port: 8545,
},
Expand Down Expand Up @@ -53,7 +53,7 @@ export const NETWORKS_INGAME = {
UNDEFINED: undefined,
LOCAL: {
name: "local",
id: "1337",
id: "31337",
url: "http://localhost",
port: 8545,
},
Expand Down
11 changes: 11 additions & 0 deletions client/src/gamedata/authors.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@
"https://github.com/fefeupz"
],
"donate": "0x00000000000d86e4837ba41dacde4b8713d5ccac"
},
"GustavoDeps":{
"name": [
"Gustavo Deps"
],
"websites": [
"https://www.linkedin.com/in/gustavo-deps/"
],
"emails": [
"[email protected]"
]
}
}
}
3 changes: 2 additions & 1 deletion client/src/gamedata/deploy.holesky.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"27": "0xC5e91F614a5D8b2bcBB062Dca27857EF32288ad5",
"28": "0x199E2090f6751B542861df7fCA58cB9144aF01eD",
"29": "0x1bFb120Ac1361ece092FC64BD8ECdb3244463071",
"30": "x",
"30": "0x716747Fbc1FcE4c36F2B369F87aDB5D4580e807f",
"31": "0x32FFB8d4244B350F5D3E074e9b731A135531B975",
"ethernaut": "0xB877915d8Ba049e7cAFc1525F85CEc322A362767",
"implementation": "0x86C8eC9b2bE1600571183eE157C7eb3B96a5c3FF",
"proxyAdmin": "0x8f3189256cb686D0aCD642bAa3982Fda156fB01D",
Expand Down
1 change: 1 addition & 0 deletions client/src/gamedata/deploy.sepolia-arbitrum.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"28": "0x6A77737803b581B79D5323016f59DFbfE681b336",
"29": "0xd4e6B977d9Dea283797AaD71a09eC65DfdAc98f5",
"30": "0xA62fE5344FE62AdC1F356447B669E9E6D10abaaF",
"31": "0x7ae0655F0Ee1e7752D7C62493CEa1E69A810e2ed",
"ethernaut": "0xD991431D8b033ddCb84dAD257f4821E9d5b38C33",
"implementation": "0x42E7014a9D1f6765e76fA2e69532d808F2fe27E3",
"proxyAdmin": "0xBd886a37faD1f596221f33ca568122815ED48c81",
Expand Down
1 change: 1 addition & 0 deletions client/src/gamedata/deploy.sepolia-optimism.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"28": "0x2aa5685ffd9e8e4897caf92855C1959d82DA5E36",
"29": "0xDCa6065818935c33D6AF9AbDB7d5f679BB43508A",
"30": "0x5c7Fe23aeFc74E85E99EB8235807fE53bcC9c58f",
"31": "0x708f096A1AE25dD1b2De076fA90F5158bb01D209",
"ethernaut": "0xD991431D8b033ddCb84dAD257f4821E9d5b38C33",
"implementation": "0x50E1785EeE794253c5E33B8fE123e77124736e38",
"proxyAdmin": "0x492e18ddBd7591638453d2f1B1847F86711105C8",
Expand Down
1 change: 1 addition & 0 deletions client/src/gamedata/deploy.sepolia.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"28": "0x653239b3b3E67BC0ec1Df7835DA2d38761FfD882",
"29": "0xb2aBa0e156C905a9FAEc24805a009d99193E3E53",
"30": "0xd459773f02e53F6e91b0f766e42E495aEf26088F",
"31": "0xB99f27b94fCc8b9b6fF88e29E1741422DFC06224",
"ethernaut": "0xa3e7317E591D5A0F1c605be1b3aC4D2ae56104d6",
"implementation": "0x49662cAeF8386f84d99873c34280E24d3e742e4f",
"proxyAdmin": "0x545d848827bD9e0E30794a9E53f5ab04EA71d78a",
Expand Down
12 changes: 12 additions & 0 deletions client/src/gamedata/en/descriptions/levels/stake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Stake is safe for staking native ETH and ERC20 WETH, considering the same 1:1 value of the tokens. Can you drain the contract?

To complete this level, the contract state must meet the following conditions:

* The `Stake` contract's ETH balance has to be greater than 0.
* `totalStaked` must be greater than the `Stake` contract's ETH balance.
* You must be a staker.
* You staked balance must be 0.

Things that might be useful:
* [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) specification.
* [OpenZeppelin contracts](https://github.com/OpenZeppelin/openzeppelin-contracts)
5 changes: 5 additions & 0 deletions client/src/gamedata/en/descriptions/levels/stake_complete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Congratulations, you have cracked the `Stake` machine!

When performing low-level calls to external contracts, it is important to properly validate external call returns to determine whether the call reverted.

For more info, check out [EEA EthTrust [S] Check External Calls Return](https://entethalliance.github.io/eta-registry/security-levels-spec.html#req-1-check-return) requirement, and always use [SafeERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol) when interacting with external ERC-20 tokens.
15 changes: 15 additions & 0 deletions client/src/gamedata/gamedata.json
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,21 @@
"compilerVersion": "v0.6.12+commit.27d51765",
"runs": "1000"
}
},
{
"name": "Stake",
"created": "2024-04-13",
"difficulty": "6",
"description": "stake.md",
"completedDescription": "stake_complete.md",
"levelContract": "StakeFactory.sol",
"instanceContract": "Stake.sol",
"revealCode": true,
"deployParams": [],
"deployFunds": 0,
"deployId": "31",
"instanceGas": 750000,
"author": "GustavoDeps"
}
]
}
12 changes: 12 additions & 0 deletions client/src/gamedata/pt_br/descriptions/levels/stake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Stake é cofre para fazer stake de ETH nativo e ERC20 WETH, considerando o mesmo valor 1:1 dos tokens. Você pode drenar o contrato?

Para completar este nível, o estado do contrato deve atender às seguintes condições:

* O saldo em ETH do contrato `Stake` deve ser maior que 0.
* `totalStaked` deve ser maior que o saldo em ETH do contrato `Stake`.
* Você deve ser um staker.
* Seu saldo estacado deve ser 0.

Coisas que podem ser úteis:
* A especificação [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)
* Os contratos da [OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/tree/master/contracts)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Parabéns, você conseguiu desvendar a máquina `Stake`!

Ao realizar chamadas de baixo nível para contratos externos, é importante validar corretamente os retornos das chamadas externas para determinar se a chamada foi revertida.

Para mais informações, confira o requisito [EEA EthTrust [S] Check External Calls Return](https://entethalliance.github.io/eta-registry/security-levels-spec.html#req-1-check-return), e sempre use [SafeERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol) ao interagir com tokens externos ERC-20."
25 changes: 15 additions & 10 deletions client/src/gamedata/pt_br/descriptions/levels/telephone_complete.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
Embora este exemplo possa ser simples, confundir `tx.origin` com `msg.sender` pode levar a ataques do tipo phishing, como [este](https://blog.ethereum.org/2016/06/24/security-alert-smart-contract-wallets-created-in-frontier-are-vulnerable-to-phishing-attacks/).
Embora este exemplo sejá bem simples, podemos esquecer de checar o retorno `success bool` quando realizamos chamadas externas, neste caso de `transfer` para saber se a mesma ocorreu da forma que deveria, como [este](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-transfer)

Um exemplo de um possível ataque é descrito abaixo.

1) Use `tx.origin` para determinar de quem os tokens serão transferidos, por exemplo:
1) Use `ERC(Stake.WETH()).approve(address(Stake), 1 ether)` para determinar de o contrato pode transferir os tokens que você "possui"

2) Agora use a função `Stake.StakeWETH(uint256 amount)` com qualquer valor para amount que seja maior que o `Stake.balance` e veja seus `UserStake` points subirem sem haver transferido quaiquer fundos e extraia através de `Unstake(uint256 amount)` com amount igual a `Stake.balance` e veja o ETH indo para sua carteira, por exemplo:

```
function transfer(address _to, uint _value) {
tokens[tx.origin] -= _value;
tokens[_to] += _value;
function () payable {
ERC20(Stake.WETH).approve(stakeAddress, 1000000000000000000);
Stake.StakeWETH(1000000000000000000);
Stake.Unstake(Stake.balance);
}
```

2) O hacker faz com que a vítima envie fundos para um contrato malicioso que chama a função de transferência do contrato de token, por exemplo:
3) Nesse cenário, `UserStake[attackerContractAddress]` será maior que o valor transferido ao contrato, permitindo que a função `Unstake(uint256 amount)` seja chamada, adicione também uma transferência dos fundos para a sua wallet, assim os fundos irão para a sua wallet e o contrato será drenado, por exemplo:

```
function () payable {
token.transfer(attackerAddress, 10000);
ERC20(Stake.WETH).approve(stakeAddress, 1000000000000000000);
Stake.StakeWETH(1000000000000000000);
Stake.Unstake(Stake.balance);
(bool success, bytes memory return) = payable(msg.sender).call{value: address(this).balance}("")
require (success)
}
```

3) Nesse cenário, `tx.origin` será o endereço da vítima (enquanto `msg.sender` será o endereço do contrato malicioso), resultando na transferência de fundos da vítima para o hacker.
```
2 changes: 1 addition & 1 deletion client/src/middlewares/setNetwork.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const setNetwork = store => next => action => {

export function onPredeployedNetwork(id) {
let onRightNetwork = false;
let allNetworkIds = Object.keys(constants.ID_TO_NETWORK).filter(id => constants.ID_TO_NETWORK[id] !== constants.NETWORKS.LOCAL.name).map((key) => Number(key))
let allNetworkIds = Object.keys(constants.ID_TO_NETWORK).map((key) => Number(key))
onRightNetwork = allNetworkIds.includes(Number(id));
return onRightNetwork;
}
Expand Down
1 change: 1 addition & 0 deletions contracts/out/Stake.sol/Stake.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions contracts/out/Stake.t.sol/TestStake.json

Large diffs are not rendered by default.

Loading

0 comments on commit f4ae6c8

Please sign in to comment.