Solutions to Eko 2022 CTF challenges ⛳️
🚧 WIP
Anyone can become leader of the Mothership.
The CleaningModule
and the RefuelModule
do not have any access control, so they can be called by anyone, allowing to update storage variables from the SpaceShip
contract through delegatecall
, and thus escalating priviliges until getting the leadership of the Mothership.
- Deploy a
LeadershipModuleAttacker
contract to override theisLeaderApproved
of theLeadershipModule
module - Deploy an attacker contract to perform the exploit
- For each ship (except for one), call the
replaceCleaningCompany
method with the attacker contract address, to assign that address as the captain - The remaining ship will be a candidate to become the leader:
- Reset the captain by calling
replaceCleaningCompany
from theCleaningModule
with the0x0
address - Assign the attacker as a crew member by calling
addAlternativeRefuelStationsCodes
from theRefuelModule
- Assign the captain and make the mothership know about it by calling
spaceShip.askForNewCaptain
with the attacker address
- Reset the captain by calling
- Everything is set up now to promote to the attacker to a leader. All spaceships have an approve module that returns true. There is one spaceship with a captain that can be promoted to leader
- Call
mothership.promoteToLeader
with the attacker address - Call
mothership.hack
Anyone can withdraw all the funds from the contract.
The contracts implement a proxy pattern incorrectly. The Jackpot
contract is the one holding the funds, and can be reinitialized by anyone by calling the initialize
method again and overwrite the original proxyJackpot
address used for access control.
- Obtain the address of the
Jackpot
contract - Call initialize to set the attacker as the new "jackpotProxy" to bypass validation
- Withdraw funds
The contract uses block.timestamp
, blockhash
, and block.number
for randomization.
The contract uses deterministic variables for randomization and the result can be calculated beforehand.
- Create an attacker contract
- Precalculate the "random" value
- Call the target contract with the precalculated slot value
Anyone can get root access by registering a new identifier.
The security of the identifiers relies on keccak256(abi.encodePacked(user, salt))
, but it doesn't consider that the encodePacked
method will return the same results for multiple strings concatenated, like ("ROOT", "ROOT")
and ("ROO","TROOT")
.
- Register an identifier with
("ROO","TROOT")
or any other equivalent pair that wasn't used - Call the
write
method to override thevictory
variable
-
First step is to make the contract not
invincible
. To do so, we need the contract balance to be 1 wei. This can be achieved by emptying the contract balance, and then sending it exactly one wei. -
Then calculate the
spell
andmagic
to be able to call thekill
method internally viadestroyIt
- Empty the contract balance by calling any function not implemented, which will get to the
fallback
function - Send 1 wei to the contract (by selfdestructing another contract for example)
- Convert the
spell
frombytes32
tostring
- Calculate the
magic
by calculating the reverse function ofkedavra = abi.encodePacked(bytes4(bytes32(uint256(spellInBytes) - magic)))
- Call the
destroyIt
method - It will call the
kill
method internally
Anyone can get tickets without waiting any time.
-
The
updateWaitTime
method is vulnerable to an overflow onwaitlist[msg.sender] += uint40(_time);
-
The
joinRaffle
method uses deterministic "randomness" onuint256 randomNumber = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));
- Join the waitlist
- Call the
updateWaitTime
method with a value that makes it overflow - Call
joinRaffle
with the precalculated "random" value
The contract can lose all funds due to error with remainder.
There is a miscalculation regarding remainders when the amount is lower than the oracle price in require(amountGMEin / ORACLE_TSLA_GME == amountTSLAout, "Invalid price");
. Leading to cases like 49 / 50 == 0
being a valid case. This can be used to exploit prices.
- Sell all TSLA stonks
- Buy TSLA stonks with an GME amount lower than the oracle price, for 0 TSLA
- Repeat until the TSLA balance is 0
The goal is to make the useNozzle
fail. In order to do that nozzle.insert
has to consume all the remaining gas for the function.
- Make a call to the contract limiting the gas or create a
nozzle.insert
function that consumes almost all gas to make theuseNozzle
fail
The main challenges here are
-
Generate a contract with an address that satisfies
address % 100 == 10
. This can be done by trying with different salt values -
Implement the corresponding functions of the attacker contract that will be called
-
The
passTheBall
requirements are bypassed becauserequire(msg.sender.code.length == 0, "Only EOA players")
does not apply on contract creation, where the code length is 0
- Create a contract with an address that satisfies
address % 100 == 10
- Call
passTheBall
to set themsg.sender
as theplayer
on the storage - Calculate the
owner
on the new contract with thedeployer
of the original contract - Call
shoot
on the attacker contract- It will check the
getBallPossesion
method - It will check that the
owner
is correct - It will check the
handOfGod
where we set thegoals
variable to 2, and return the22_06_1986
value
- It will check the
This is a metamorphic contract. It looks like it should re-deploy the same contract every time, but in fact, after being destructed it can deploy a different runtime code implementation. The trick is to deploy a new contract that modifies the underlying storage of the proxy.
- Create an implementation contract implementing the
reBorn
method, but assigning the attacker as theowner
- Destroy the original implementation by calling
capture
on it with any value - Deploy the new implementation with the
Laboratory.reBorn
method
- Meal tokens can be minted for free
If the oracle is not set, it is set as 0, which is the response from the ecrecover
when it fails.
- Create an oracle price calldata with price of 0
- Create a signature with a valid
v
value - Mint tokens for free