player "0x3A22d5B698CCb49b51B7B4D953a8Abb8F8a562cE"
player "0x3A22d5B698CCb49b51B7B4D953a8Abb8F8a562cE"
Owner Address 0x3c34A342b2aF5e885FcaA3800dB5B205fEfa3ffB
Send eth without data to hit recieve
and bypass contribute() require(msg.value < 0.001 ether);
Send eth to fal1out function and become owner. sollidity 0.6 constructor is declared by giving it same name as contract. but fallout != fal1out so fal1out is public payable and has no onlyowner + owner = msg.sender.
copy function to exploit contract if true call coin_flip contract. repeat till 10 consecutive wins.
create a contract which calls changeowner on this contract. profit!!!
simple overflow.
delegate call missuse exploiting context. send 0xdd365b8b via low lvl transaction with your own acc.
selfdestruct and boom!!!
web3.eth.getStorageAt(addr, 1, console.log)
> null 0x412076657279207374726f6e67207365637265742070617373776f7264203a29
web3.utils.toAscii("0x412076657279207374726f6e67207365637265742070617373776f7264203a29")
'A very strong secret password :)'
exploit contract becomes king but has no recieve or fallback function breaking the recieve function in king contract
Straightforward reentrancy attack.
send false when function is first called and true when it is called again.
// get the entry of data array at index 2.
await web3.eth.getStorageAt(contract.address, 5, console.log)
"0x14690fdf857625e4fe6f798d6ad3c7dbf82a6b1a6c9c082c0c6cfca4d394c4b1"
// convert 32 bytes to 16. (strip the last 16 bytes) and get the key
0x14690fdf857625e4fe6f798d6ad3c7db
basically telephone challenge.
gasleft() % 8191 == 0; ????????
BruteForce gas to send in local foundry env.
require(uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)), "GatekeeperOne: invalid gateThree part three");
means last 4 characters of player address 0x62cE
require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
means zeroes before last 4 characters of key. 0x000062cE
require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
means random non-zero 8 characters for the rest ??? 0x53a8abb8000062ce
While constructor is running for a contract EXTCODESIZE
for it's address will return 0.
msg.sender == address of exploit contract for target. xD
- From main acc approve acc2 to be able to withdraw all tokens.
- Use allowance function to verify approval.
- From acc2 use transfer from function to withdraw all tokens.
Unsafe delegate call use where it is possible to override variables of the contract. Once we can control the address of the contract it delegates to we can run our own code inside the contract.
**Smart contract version of RCE **.
address for new contract in solidity by create opcode is computed by kecchak256(relp(sender, nonce)) Sender would be instance address(0xe473eF11db6be3F1bA29F3b8bc5B4e33D75E634f)
so using the sender we can generate possible addresses and find the lost address. Once found just call self destruct to recover all eth inside contract.
low level
cause the Codex[] to underflow using retract function. then overwrite the owner state variable using revise function.
set receive function such that it consumes all gas.
Just like the elevator but view functions cannot modify state variables hence gotta improvise and use conditionals to pass the if() check in buy function.
spender - dexinstance
mytokens (start) | 1 swap usd->inr | 2 swap | 3 swap |
---|---|---|---|
usd - 10 | 9 | 8 | 7 |
inr - 10 | 11 | 11 | 11 |
dextokens(start) | ---------------- | -------- | ------ |
----------------- | ---------------- | -------- | ------ |
usd - 100 | 101 | 102 | 103 |
inr - 100 | 99 | 99 | 99 |
swap price (1 swap usd->inr)->(2 swap)->(3 swap) 1 token -> 0 -> 0 -> 0 100 token -> 98 -> 97 -> 96 swap ammount 0 hence inr not increasing in above table.
swap price (1 swap inr->usd)->(2 swap)->(3 swap) 1 token -> _ -> _-> 1 100 token -> _ -> _-> 104
!('./dexbrainstorming.png')
INCREASE THE swap amount
TO DRAIN THE DEX CONTRACT.
so basically swap from token1 to token2 and then swap from token2 to token1 and vice versa, until you can empty tokens from dex contract.
Can I deploy my own tokens??? yeah. Create Fake tokens and give dex2 just 10 so that 10 of fake 100 of dex2's.
transparent upgradeable proxy.(upgrade and admin logic handled by proxy itself)
address(uint160(maxbalance)) == admin address....... wtf..
can make proxy balance zero by delegating call to execute
. provided player and wallet contract have enough balance
- step 1: call
proposeNewAdmin(address)
and become pending admin. - step 2: call
addToWhitelist(address)
and become Whitelisted. - step 3: use multicall with ["deposit","multicall(deposit)"] as parameter to rick contract into calling deposit twice
- step 4: call execute to empty the balance.
- step 5: call set maxBalance to becom admin.
proxy's fallback function delegates call to implementation and there is a collision in state variables which can be exploited to cause chaos
Get the implementation address.
await web3.eth.getStorageAt(
contract.address,
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
);
initialize the implementation to become upgrader then call upgradeToAndCall()
func with exploit contract addr and selfdestruct func identifier.
AFTER DECUN UPDATE (courtesy of https://github.com/Ching367436)
The selfdestruct function will no longer remove the contract code after the upgrade, so the above solution will not work.
so we would need to use a contract to call ethernaut contract to create an instance and the in the same transaction perform the above exploit to solve the challenge. but still won't appear on the ethernaut client side.
my solution txn:
https://sepolia.etherscan.io/tx/0x05599a912ee760312d6a5585762887b4bc499022b100cd31c73867bb7d9666ab
LegacyToken can delegate to new contract so if legacy token delegates to DET, DET can be swept from vault. bot should monitor delegateToNewContract function of Legacytoken.
Backdoor function notify
inside of coin contract can be leveraged to pass custom error value like NotEnoughBalance()
which will make the good samaritan contract jump into catch
statement and give us tokens.
StraightForwad. get password from trick's storage -> put >0.001 eth into gatekeeper-> deploy a contract without fallback or recieve and call constructor for gatekeeper. enter.
why did I not directly fund gatekeeper and used a hacky way instead?? realized later lol.
assembly {
calldatacopy(selector, 68, 4) // grab function selector from calldata
}
Copies 4 bytes from the calldata from the 68th byte to write to memory at location selector
. where,
selector
can only be offSelector
Copies 4 bytes from the calldata from the 68th byte means we can control the calldata as long as 0x20606e15
is present at 68th byte of calldata.
await sendTransaction({
from: player,
to: contract.address,
data: "0x30c13ade0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000020606e1500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000476227e1200000000000000000000000000000000000000000000000000000000",
});
await contract.switchOn(); // should return true
> ### THINGS TO AVOID <br>
>
> Inline assembly might have a quite high-level look, but it actually is extremely low-level. Function calls, loops, ifs and switches are converted by simple rewriting rules and after that, the only thing the assembler does for you is re-arranging functional-style opcodes, counting stack height for variable access and removing stack slots for assembly-local variables when the end of their block is reached.
While solidity does enforce type-checking EVM does not.
By sending a low level call to registerTreasury(uint8)
function we can bypass the restriction of uint8 type as it is enforced by solidity and not by the EVM.
ABI encoder v2 impliments type checking safegaurds for contracts compiled with sol v0.8.0 onwards
0xdd62ed3e , 0x23b872dd
are vuln to collisions.
0x23b872dd: transferFrom(address,address,uint256)
0xdd62ed3e: allowance(address,address)
contract does not handle false return for low level calls.
can stake weth without having any.
- step 1: use another EOA to fund stake with 0.002 ether.
- step 2: sendETH 8000000000000000 wei
- step 3: send Weth "2000000000000000" weth
- step 4: unstake 9900000000000000 such that contract eth = 0.0001
- step 5: unstake remaining stake
this works because (bool success, ) = payable(msg.sender).call{value : amount}("");
in function Unstake(uint256)
will give you ether if contract has enough balance othwerwise it will pay in weth